package net.oriol.shared.math;

/**
 * Oriol's Float4 vector math class
 * 
 * @author A.Weissflog
 *
 */
public class Float4 {
	
	float x, y, z, w;
	
	/**
	 * Default constructor.
	 */
	Float4()
	{
		this.x = 0.0f;
		this.y = 0.0f;
		this.z = 0.0f;
		this.w = 0.0f;
	}
	
	/**
	 * Construct from components.
	 * 
	 * @param _x
	 * @param _y
	 * @param _z
	 * @param _w
	 */
	Float4(float _x, float _y, float _z, float _w)
	{
		this.x = _x;
		this.y = _y;
		this.z = _z;
		this.w = _w;
	}
	
	/**
	 * Copy constructor.
	 * @param rhs
	 */
	Float4(Float4 rhs)
	{
		this.x = rhs.x;
		this.y = rhs.y;
		this.z = rhs.z;
		this.w = rhs.w;
	}
	
	/**
	 * Test for equality with tolerance
	 * @param rhs
	 * @param tol
	 * @return
	 */
	boolean Equal(Float4 rhs, float tol)
	{
		return (Math.abs(this.x - rhs.x) <= tol) &&
			   (Math.abs(this.y - rhs.y) <= tol) &&
			   (Math.abs(this.z - rhs.z) <= tol) &&
			   (Math.abs(this.w - rhs.w) <= tol);
	}
	
	/**
	 * Set components.
	 * 
	 * @param _x
	 * @param _y
	 * @param _z
	 * @param _w
	 */
	void Set(float _x, float _y, float _z, float _w)
	{
		this.x = _x;
		this.y = _y;
		this.z = _z;
		this.w = _w;
	}
	
	/**
	 * Compute length of vector.
	 * @return
	 */
	float Length()
	{
		return (float) Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z + this.w*this.w);
	}
	
	/**
	 * Compute length-squaed of vector.
	 * @return
	 */
	float LengthSq()
	{
		return this.x*this.x + this.y*this.y + this.z*this.z + this.w * this.w;
	}

	/**
	 * Compute reciprocal inplace.
	 */
	void Reciprocal()
	{
		this.x = 1.0f / this.x;
		this.y = 1.0f / this.y;
		this.z = 1.0f / this.z;
		this.w = 1.0f / this.w;
	}
	
	/**
	 * Inplace component-wise add.
	 * @param v
	 */
	void Add(Float4 v)
	{
		this.x += v.x;
		this.y += v.y;
		this.z += v.z;
		this.w += v.w;
	}
	
	/**
	 * Add 2 Float4 vectors, return new Float4 vector.
	 */
	static Float4 Add(Float4 v0, Float4 v1)
	{
		Float4 v = new Float4(v0);
		v.Add(v1);
		return v;
	}
	
	/**
	 * Inplace component-wise multiply.
	 * @param v
	 */
	void Mul(Float4 v)
	{
		this.x *= v.x;
		this.y *= v.y;
		this.z *= v.z;
		this.w *= v.w;
	}

	/**
	 * Multiply 2 Float4 vectors, return new Float4
	 * @param v0
	 * @param v1
	 * @return
	 */
	static Float4 Mul(Float4 v0, Float4 v1)
	{
		return new Float4(v0.x*v1.x, v0.y*v1.y, v0.z*v1.z, v0.w*v1.w);
	}
	
	/**
	 * Inplace component-wise abs.
	 */
	void Abs()
	{
		this.x = Math.abs(this.x);
		this.y = Math.abs(this.y);
		this.z = Math.abs(this.z);
		this.w = Math.abs(this.w);
	}
	
	/**
	 * Component-wise abs, return new Float4.
	 * @param rhs
	 */
	static Float4 Abs(Float4 rhs)
	{
		Float4 v = new Float4(rhs);
		v.Abs();
		return v;
	}
	
	/**
	 * Inplace 3-component cross-product.
	 */
	void Cross3(Float4 v1)
	{
		float _x = this.y * v1.z - this.z * v1.y;
		float _y = this.z * v1.x - this.x * v1.z;
		float _z = this.x * v1.y - this.y * v1.z;
		this.x = _x;
		this.y = _y;
		this.z = _z;
		this.w = 0.0f;
	}
	
	/**
	 * 3-component cross product of 2 vectors, returns new vector.
	 *  
	 * @param v0
	 * @param v1
	 * @return
	 */
	static Float4 Cross3(Float4 v0, Float4 v1)
	{
		Float4 v = new Float4(v0);
		v.Cross3(v1);
		return v;
	}
	
	/**
	 * Compute 3-component dot-product of 2 vectors.
	 * 
	 * @param v0
	 * @param v1
	 * @return
	 */
	static float Dot3(Float4 v0, Float4 v1)
	{
		return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z;
	}

	/**
	 * Inplace linear-interpolation.
	 * 
	 * @param rhs
	 * @return 
	 */
	void Lerp(Float4 rhs, float s)
	{
		this.x = this.x + ((rhs.x - this.x) * s);
		this.y = this.y + ((rhs.y - this.y) * s);
		this.z = this.z + ((rhs.z - this.z) * s);
		this.w = this.w + ((rhs.w - this.w) * s);
	}
	
	/**
	 * Linear interpolation of 2 vectors, returns new Float4.
	 * @param v0
	 * @param v1
	 * @param s
	 * @return
	 */
	static Float4 Lerp(Float4 v0, Float4 v1, float s)
	{
		Float4 v = new Float4(v0);
		v.Lerp(v1, s);
		return v;
	}
	
	/**
	 * Inplace component-wise maximise.
	 * @param rhs
	 */
	void Maximize(Float4 rhs)
	{
	    this.x = (this.x > rhs.x) ? this.x : rhs.x;
	    this.y = (this.y > rhs.y) ? this.y : rhs.y;
	    this.z = (this.z > rhs.z) ? this.z : rhs.z;
	    this.w = (this.z > rhs.w) ? this.w : rhs.w;
	}
	
	/**
	 * Component-wise maximize of 2 vectors, returns new vector
	 * @param v0
	 * @param v1
	 * @return
	 */
	static Float4 Maximize(Float4 v0, Float4 v1)
	{
		Float4 v = new Float4(v0);
		v.Maximize(v1);
		return v;
	}
	
	/**
	 * Inplace component-wise minimize.
	 * @param rhs
	 */
	void Minimize(Float4 rhs)
	{
	    this.x = (this.x < rhs.x) ? this.x : rhs.x;
	    this.y = (this.y < rhs.y) ? this.y : rhs.y;
	    this.z = (this.z < rhs.z) ? this.z : rhs.z;
	    this.w = (this.z < rhs.w) ? this.w : rhs.w;
	}
	
	/**
	 * Component-wise minimize of 2 vectors, returns new vector
	 * @param v0
	 * @param v1
	 * @return
	 */
	static Float4 Minimize(Float4 v0, Float4 v1)
	{
		Float4 v = new Float4(v0);
		v.Minimize(v1);
		return v;
	}
	
	/**
	 * Inplace normalize.
	 */
	void Normalize()
	{
		float l = this.Length();
		if (l > 0.0f)
		{
			float oneDivL = 1.0f / l;
			this.x *= oneDivL;
			this.y *= oneDivL;
			this.z *= oneDivL;
			this.w *= oneDivL;
		}
	}
	
	/**
	 * Return new normalized vector.
	 */
	static Float4 Normalize(Float4 rhs)
	{
		Float4 v = new Float4(rhs);
		v.Normalize();
		return v;
	}
}
